package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"

	flag "github.com/spf13/pflag"
)

type spArgs struct {
	startPage  int
	endPage    int
	inFilename string
	pageLen    int
	pageType   string
	printDest  string
}

var progname string

func main() {
	progname = os.Args[0]
	var sa spArgs
	sa.init()
	if err := fmtArgs(&sa); err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "%s", err)
		return
	}
	if err := procIO(sa); err != nil {
		fmt.Fprintf(os.Stderr, "%s", err)
		return
	}
}

func (sa *spArgs) init() {
	flag.IntVarP(&sa.startPage, "start", "s", -1, "start page(>1)")
	flag.IntVarP(&sa.endPage, "end", "e", -1, "end page(>=start_page)")
	flag.IntVarP(&sa.pageLen, "len", "l", 72, "page len")
	flag.StringVarP(&sa.printDest, "dest", "d", "", "print destination")
	flag.StringVarP(&sa.pageType, "type", "f", "l", "'l' for lines-delimited, 'f' for form-feed-delimited. default is 'l'")
	flag.Lookup("type").NoOptDefVal = "f"
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "USAGE: %s -sstartPage -eendPage [ -f | -llines_per_page ] [ -ddest ] [ in_filename ]\n", progname)
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) > 0 {
		sa.inFilename = flag.Args()[0]
	}
}

func fmtArgs(sa *spArgs) error {
	const MAXINT = int(^uint(0) >> 1)
	if sa.startPage < 1 || sa.startPage > MAXINT {
		err := MyError{}
		err.desc = fmt.Sprintf("the startPage is invalid")
		return &err
	}
	if sa.endPage < 1 || sa.endPage > MAXINT || sa.endPage < sa.startPage {
		err := MyError{}
		err.desc = fmt.Sprintf("the endPage is invalid")
		return &err
	}

	if sa.pageLen < 1 || sa.pageLen > MAXINT {
		err := MyError{}
		err.desc = fmt.Sprintf("the pageLen is invalid")
		return &err
	}

	if sa.inFilename != "" {
		_, err := os.Stat(sa.inFilename)
		if os.IsNotExist(err) {
			err := MyError{}
			err.desc = fmt.Sprintf("file \"%s\" doesn't exist", sa.inFilename)
			return &err
		}
	}
	return nil
}

func procIO(sa spArgs) error {
	fin := os.Stdin
	if sa.inFilename != "" {
		var err error
		fin, err = os.Open(sa.inFilename)
		if err != nil {
			err := MyError{}
			err.desc = fmt.Sprintf("%s: could not open input file \"%s\"", progname, sa.inFilename)
			return &err
		}
	}
	defer fin.Close()
	finBuf := bufio.NewReader(fin)

	foutBuf := io.WriteCloser(os.Stdout)
	if sa.printDest != "" {
		// exec.Command("lp", "-d", sa.printDest)
		// 无法使用 lp 指令，因此改用 cat 指令进行测试
		cmd := exec.Command("cat")
		_, err := os.Stat(sa.printDest)
		if os.IsNotExist(err) {
			cmd.Stdout, err = os.OpenFile(sa.printDest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0)
			os.Chmod(sa.printDest, 0664)
		} else {
			cmd.Stdout, err = os.OpenFile(sa.printDest, os.O_APPEND|os.O_WRONLY|os.O_TRUNC, 0)
		}
		if err != nil {
			err := MyError{}
			err.desc = fmt.Sprintf("%s: can't open file %s", progname, sa.printDest)
			return &err
		}
		foutBuf, err = cmd.StdinPipe()
		if err != nil {
			err := MyError{}
			err.desc = fmt.Sprintf("%s: can't open pipe to \"lp -d%s\"",
				progname, sa.printDest)
			return &err
		}
		defer foutBuf.Close()
		cmd.Start()
	}

	pageCtr := 1
	if sa.pageType == "l" {
		lineCtr := 0
		for {
			line, err := finBuf.ReadString('\n')
			if err != nil {
				break
			}
			lineCtr++
			if lineCtr > sa.pageLen {
				pageCtr++
				lineCtr = 1
			}
			if pageCtr >= sa.startPage && pageCtr <= sa.endPage {
				fmt.Fprintf(foutBuf, "%s", line)
			}
		}
	} else {
		for {
			page, err := finBuf.ReadString('\f')
			if err != nil {
				break
			}
			if pageCtr >= sa.startPage && pageCtr <= sa.endPage {
				fmt.Fprintf(foutBuf, "%s", page)
			}
			pageCtr++
		}
	}
	return nil
}
